package com.example.demo.ws;

import com.example.demo.common.Message;
import com.example.demo.utils.MessageUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


@Component
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)
public class ChatEndpoint {
    // 用来存储每一个客户端对象对应的ChatEndpoint对象
    private static ConcurrentHashMap<String,ChatEndpoint> onlineClient = new ConcurrentHashMap<>();
    // 声明session对象，通过该对象可以发生消息给指定的客户端
    private Session session;
    // 声明Httpsession对象，里面记录了用户相关信息
    private HttpSession httpSession;

    /**
     * 连接建立时调用
     * @param session 当前连接的会话
     * @param config Endpoint的配置对象
     */
    @OnOpen
    public void onOpen(Session session,EndpointConfig config) {
        try {
            // 初始化当前Endpoint的链接会话
            this.session = session;
            // 初始化httpSession
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            this.httpSession = httpSession;
            // 将当前连接的ChatEndpoint对象存储到onlineClient容器中
            String username = (String) httpSession.getAttribute("user");
            onlineClient.put(username,this);
            // 将在线用户推送给所有客户端
            // 1.获取消息
            String message = MessageUtils.getMessage(true, null, getNames());
            // 2.将消息广播给所有用户
            broadcastAllUsers(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void broadcastAllUsers(String message) {
        Set<String> names = getNames();
        try {
            for (String name : names) {
                // 获取当前用户的ChatEndpoint对象
                ChatEndpoint client = onlineClient.get(name);
                // 使用session发送信息
                client.session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Set<String> getNames() {
        return onlineClient.keySet();
    }

    /**
     * 接收到客户端数据后调用
     * @param message 消息信息
     * @param session 当前连接的会话
     */
    @OnMessage
    public void onMessage(String message,Session session) {
        try {
            // 导入jackson核心类
            ObjectMapper objectMapper = new ObjectMapper();
            // 将JSON字符串序列化为Message对象
            Message mess = objectMapper.readValue(message, Message.class);
            // 取出接收人
            String toName = mess.getToName();
            // 取出信息
            String sendMess = mess.getMessage();
            // 找到发送人姓名
            String fromName = (String) httpSession.getAttribute("user");
            // 进行消息构造
            String finalMess = MessageUtils.getMessage(false, fromName, sendMess);
            // 根据接收人找到对应的连接
            ChatEndpoint chatEndpoint = onlineClient.get(toName);
            if (chatEndpoint == null) {
                // 这里可以抛出异常
                // ...
                // 打印日志
                System.out.println("找不到对应的客户端！userName = "+toName);
                return;
            }
            // 使用对应的会话Session发送给对应的客户端
            chatEndpoint.session.getBasicRemote().sendText(finalMess);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 连接过程中发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("websocket连接出错，错误原因:"+error.getMessage());
    }

    /**
     * 关闭连接时调用
     */
    @OnClose
    public void onClose(Session session) {
        try {
            // 1.清除容器中的该ChatEndpoint对象
            String username = (String) httpSession.getAttribute("user");
            onlineClient.remove(username);
            // 2.向所有客户端广播下线通知
            Set<String> names = getNames();
            // 3.构造消息
            String message = MessageUtils.getMessage(true,null,getNames());
            // 4.广播给其他用户
            broadcastAllUsers(message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
